package com.hzc.springchan.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hzc.springchan.bean.exception.CException;
import com.hzc.springchan.enums.PrivError;
import com.hzc.springchan.service.ITokenService;
import com.hzc.springchan.util.JSONUtils;
import com.hzc.springchan.util.LogUtils;
import com.hzc.springchan.util.RedisUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.Calendar;
import java.util.Map;

/**
 * JSON Web Token 颁发/校验服务类
 * @author chen990627
 * @date 2022/10/19
 */
@Service("jwtokenService")
public class JWTokenServiceImpl implements ITokenService {

    private final static String REDIS_KEY = "token:userid-";

    /**
     * 用于JWT进行签名加密的秘钥
     */
    private final static String SECRET = "@welcome-to-springchan";

    @Override
    public String generate(Map<String, String> map, String mapIdentKey) {

        LogUtils.info(this, "generate token map: {}", JSONUtils.toJSON(map));

        JWTCreator.Builder builder = JWT.create();
        // 将map内的信息传入JWT的payload中
        map.forEach((k, v) -> {
            builder.withClaim(k, v);
        });

        // 设置JWT令牌的过期时间为30min
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, 1800);
        builder.withExpiresAt(instance.getTime());

        // 设置签名并返回token
        String token = builder.sign(Algorithm.HMAC256(SECRET));

        RedisUtils.setString(REDIS_KEY + map.get(mapIdentKey), token, 1800);
        return token;
    }

    @Override
    public void validate(String token, String mapIdentKey) {

        Map<String, Claim> claimMap = getClaims(token);
        String key = REDIS_KEY + getValue(token, mapIdentKey, String.class, mapIdentKey);
        if (ObjectUtils.isEmpty(RedisUtils.getString(key))) {
            LogUtils.info(this, "用户已退出登录");
            throw new CException(PrivError.TOKEN_EXPIRE_LOGOUT);
        }
    }

    @Override
    public Map<String, Claim> getClaims(String token) {

        DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        Map<String, Claim> claimMap = jwt.getClaims();
        return claimMap;
    }

    @Override
    public <T> T getValue(String token, String param, Class<T> clazz, String mapIdentKey) {

        Map<String, Claim> claimMap = getClaims(token);

        String redisKey = REDIS_KEY + claimMap.get(mapIdentKey).as(String.class);
        if (ObjectUtils.isEmpty(RedisUtils.getString(redisKey))) {
            LogUtils.info(this, "用户已退出登录");
            throw new CException(PrivError.TOKEN_EXPIRE_LOGOUT);
        }

        Claim claim = claimMap.get(param);
        return claim == null ? null : claim.as(clazz);
    }

    @Override
    public void expire(String token, String mapIdentKey) {

        String redisKey = REDIS_KEY + getValue(token, mapIdentKey, String.class, mapIdentKey);
        RedisUtils.delString(redisKey);
    }
}
